package com.faiz.picturebed.registry;

import com.faiz.picturebed.common.PictureType;
import com.faiz.picturebed.registry.beanregistry.AbstractBeanRegistry;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.SmartInitializingSingleton;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

@Component
public class BeanRegistrantAnnotationBeanPostProcessor implements BeanPostProcessor, Ordered, BeanFactoryAware, SmartInitializingSingleton {

    private BeanFactory beanFactory;
    
    private final Map<Class<? extends AbstractBeanRegistry>, List<Object>> registryBeanMap = new ConcurrentHashMap<>();

    // 让处理类在IOC容器中最后加载
    @Override
    public int getOrder() {
        return LOWEST_PRECEDENCE;
    }

    @Override
    public void setBeanFactory(BeanFactory beanFactory) {
        this.beanFactory = beanFactory;
    }

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return BeanPostProcessor.super.postProcessBeforeInitialization(bean, beanName);
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        BeanRegistrant registryBean = bean.getClass().getDeclaredAnnotation(BeanRegistrant.class);

        if (!ObjectUtils.isEmpty(registryBean)) {
            registryBeanMap.computeIfAbsent(registryBean.registry(), k -> new ArrayList<>()).add(bean);
        }
        
        return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);
    }

    @Override
    public void afterSingletonsInstantiated() {
        for (Map.Entry<Class<? extends AbstractBeanRegistry>, List<Object>> entry : registryBeanMap.entrySet()) {
            AbstractBeanRegistry beanRegister = beanFactory.getBean(entry.getKey());
            registerBeans(beanRegister, entry.getValue());

            beanRegister.printRegisteredBeans();
        }

    }

    private void registerBeans(AbstractBeanRegistry beanRegistry, List<Object> beans) {
        for (Object bean : beans) {
            BeanRegistrant beanRegistrant = bean.getClass().getDeclaredAnnotation(BeanRegistrant.class);

            for (PictureType pictureType : beanRegistrant.pictureTypes()) {
                beanRegistry.register(pictureType, bean);
            }

        }
    }
}
